/*************************************************
*                                                *
*  EasyBMP Cross-Platform Windows Bitmap Library *
*                                                *
*  Author: Paul Macklin                          *
*   email: macklin01@users.sourceforge.net       *
* support: http://easybmp.sourceforge.net        *
*                                                *
*          file: EasyBMP.cpp                     *
*    date added: 03-31-2006                      *
* date modified: 12-01-2006                      *
*       version: 1.06                            *
*                                                *
*   License: BSD (revised/modified)              *
* Copyright: 2005-6 by the EasyBMP Project       *
*                                                *
* description: Actual source file                *
*                                                *
*************************************************/

#include "EasyBMP.h"

/* These functions are defined in EasyBMP.h */

bool EasyBMPwarnings = true;

void SetEasyBMPwarningsOff(void)
{
    EasyBMPwarnings = false;
}
void SetEasyBMPwarningsOn(void)
{
    EasyBMPwarnings = true;
}
bool GetEasyBMPwarningState(void)
{
    return EasyBMPwarnings;
}

/* These functions are defined in EasyBMP_DataStructures.h */

int IntPow(int base, int exponent)
{
    int i;
    int output = 1;
    for (i = 0; i < exponent; i++)
    {
        output *= base;
    }
    return output;
}

BMFH::BMFH()
{
    bfType = 19778;
    bfSize = 0;
    bfReserved1 = 0;
    bfReserved2 = 0;
    bfOffBits = 0;
}

void BMFH::SwitchEndianess(void)
{
    bfType = FlipWORD(bfType);
    bfSize = FlipDWORD(bfSize);
    bfReserved1 = FlipWORD(bfReserved1);
    bfReserved2 = FlipWORD(bfReserved2);
    bfOffBits = FlipDWORD(bfOffBits);
    return;
}

BMIH::BMIH()
{
    biSize = 0;
    biWidth = 0;
    biHeight = 0;
    biPlanes = 1;
    biBitCount = 0;
    biCompression = 0;
    biSizeImage = 0;
    biXPelsPerMeter = DefaultXPelsPerMeter;
    biYPelsPerMeter = DefaultYPelsPerMeter;
    biClrUsed = 0;
    biClrImportant = 0;
}

void BMIH::SwitchEndianess(void)
{
    biSize = FlipDWORD(biSize);
    biWidth = FlipDWORD(biWidth);
    biHeight = FlipDWORD(biHeight);
    biPlanes = FlipWORD(biPlanes);
    biBitCount = FlipWORD(biBitCount);
    biCompression = FlipDWORD(biCompression);
    biSizeImage = FlipDWORD(biSizeImage);
    biXPelsPerMeter = FlipDWORD(biXPelsPerMeter);
    biYPelsPerMeter = FlipDWORD(biYPelsPerMeter);
    biClrUsed = FlipDWORD(biClrUsed);
    biClrImportant = FlipDWORD(biClrImportant);
    return;
}

void BMIH::display(void)
{
    using namespace std;
    cout << "biSize: " << (int) biSize << endl
         << "biWidth: " << (int) biWidth << endl
         << "biHeight: " << (int) biHeight << endl
         << "biPlanes: " << (int) biPlanes << endl
         << "biBitCount: " << (int) biBitCount << endl
         << "biCompression: " << (int) biCompression << endl
         << "biSizeImage: " << (int) biSizeImage << endl
         << "biXPelsPerMeter: " << (int) biXPelsPerMeter << endl
         << "biYPelsPerMeter: " << (int) biYPelsPerMeter << endl
         << "biClrUsed: " << (int) biClrUsed << endl
         << "biClrImportant: " << (int) biClrImportant << endl
         << endl;
}

void BMFH::display(void)
{
    using namespace std;
    cout << "bfType: " << (int) bfType << endl
         << "bfSize: " << (int) bfSize << endl
         << "bfReserved1: " << (int) bfReserved1 << endl
         << "bfReserved2: " << (int) bfReserved2 << endl
         << "bfOffBits: " << (int) bfOffBits << endl
         << endl;
}

/* These functions are defined in EasyBMP_BMP.h */

RGBApixel BMP::GetPixel(int i, int j) const
{
    using namespace std;
    bool Warn = false;
    if (i >= Width)
    {
        i = Width - 1;
        Warn = true;
    }
    if (i < 0)
    {
        i = 0;
        Warn = true;
    }
    if (j >= Height)
    {
        j = Height - 1;
        Warn = true;
    }
    if (j < 0)
    {
        j = 0;
        Warn = true;
    }
    if (Warn && EasyBMPwarnings)
    {
        cout << "EasyBMP Warning: Attempted to access non-existent pixel;"
             << endl
             << "                 Truncating request to fit in the range [0,"
             << Width - 1 << "] x [0," << Height - 1 << "]." << endl;
    }
    return Pixels[i][j];
}

bool BMP::SetPixel(int i, int j, RGBApixel NewPixel)
{
    Pixels[i][j] = NewPixel;
    return true;
}

bool BMP::SetColor(int ColorNumber, RGBApixel NewColor)
{
    using namespace std;
    if (BitDepth != 1 && BitDepth != 4 && BitDepth != 8)
    {
        if (EasyBMPwarnings)
        {
            cout << "EasyBMP Warning: Attempted to change color table for a "
                    "BMP object"
                 << endl
                 << "                 that lacks a color table. Ignoring "
                    "request."
                 << endl;
        }
        return false;
    }
    if (!Colors)
    {
        if (EasyBMPwarnings)
        {
            cout << "EasyBMP Warning: Attempted to set a color, but the color "
                    "table"
                 << endl
                 << "                 is not defined. Ignoring request."
                 << endl;
        }
        return false;
    }
    if (ColorNumber >= TellNumberOfColors())
    {
        if (EasyBMPwarnings)
        {
            cout << "EasyBMP Warning: Requested color number " << ColorNumber
                 << " is outside the allowed" << endl
                 << "                 range [0," << TellNumberOfColors() - 1
                 << "]. Ignoring request to set this color." << endl;
        }
        return false;
    }
    Colors[ColorNumber] = NewColor;
    return true;
}

// RGBApixel BMP::GetColor( int ColorNumber ) const
RGBApixel BMP::GetColor(int ColorNumber)
{
    RGBApixel Output;
    Output.Red = 255;
    Output.Green = 255;
    Output.Blue = 255;
    Output.Alpha = 0;

    using namespace std;
    if (BitDepth != 1 && BitDepth != 4 && BitDepth != 8)
    {
        if (EasyBMPwarnings)
        {
            cout << "EasyBMP Warning: Attempted to access color table for a "
                    "BMP object"
                 << endl
                 << "                 that lacks a color table. Ignoring "
                    "request."
                 << endl;
        }
        return Output;
    }
    if (!Colors)
    {
        if (EasyBMPwarnings)
        {
            cout << "EasyBMP Warning: Requested a color, but the color table"
                 << endl
                 << "                 is not defined. Ignoring request."
                 << endl;
        }
        return Output;
    }
    if (ColorNumber >= TellNumberOfColors())
    {
        if (EasyBMPwarnings)
        {
            cout << "EasyBMP Warning: Requested color number " << ColorNumber
                 << " is outside the allowed" << endl
                 << "                 range [0," << TellNumberOfColors() - 1
                 << "]. Ignoring request to get this color." << endl;
        }
        return Output;
    }
    Output = Colors[ColorNumber];
    return Output;
}

BMP::BMP()
{
    Width = 1;
    Height = 1;
    BitDepth = 24;
    Pixels = new RGBApixel*[Width];
    Pixels[0] = new RGBApixel[Height];
    Colors = nullptr;

    XPelsPerMeter = 0;
    YPelsPerMeter = 0;

    MetaData1 = nullptr;
    SizeOfMetaData1 = 0;
    MetaData2 = nullptr;
    SizeOfMetaData2 = 0;
}

// BMP::BMP( const BMP& Input )
BMP::BMP(BMP& Input)
{
    // first, make the image empty.

    Width = 1;
    Height = 1;
    BitDepth = 24;
    Pixels = new RGBApixel*[Width];
    Pixels[0] = new RGBApixel[Height];
    Colors = nullptr;
    XPelsPerMeter = 0;
    YPelsPerMeter = 0;

    MetaData1 = nullptr;
    SizeOfMetaData1 = 0;
    MetaData2 = nullptr;
    SizeOfMetaData2 = 0;

    // now, set the correct bit depth

    SetBitDepth(Input.TellBitDepth());

    // set the correct pixel size

    SetSize(Input.TellWidth(), Input.TellHeight());

    // set the DPI information from Input

    SetDPI(Input.TellHorizontalDPI(), Input.TellVerticalDPI());

    // if there is a color table, get all the colors

    if (BitDepth == 1 || BitDepth == 4 || BitDepth == 8)
    {
        for (int k = 0; k < TellNumberOfColors(); k++)
        {
            SetColor(k, Input.GetColor(k));
        }
    }

    // get all the pixels

    for (int j = 0; j < Height; j++)
    {
        for (int i = 0; i < Width; i++)
        {
            Pixels[i][j] = *Input(i, j);
            //   Pixels[i][j] = Input.GetPixel(i,j); // *Input(i,j);
        }
    }
}

BMP::~BMP()
{
    int i;
    for (i = 0; i < Width; i++)
    {
        delete[] Pixels[i];
    }
    delete[] Pixels;
    if (Colors)
    {
        delete[] Colors;
    }

    if (MetaData1)
    {
        delete[] MetaData1;
    }
    if (MetaData2)
    {
        delete[] MetaData2;
    }
}

RGBApixel* BMP::operator()(int i, int j)
{
    using namespace std;
    bool Warn = false;
    if (i >= Width)
    {
        i = Width - 1;
        Warn = true;
    }
    if (i < 0)
    {
        i = 0;
        Warn = true;
    }
    if (j >= Height)
    {
        j = Height - 1;
        Warn = true;
    }
    if (j < 0)
    {
        j = 0;
        Warn = true;
    }
    if (Warn && EasyBMPwarnings)
    {
        cout << "EasyBMP Warning: Attempted to access non-existent pixel;"
             << endl
             << "                 Truncating request to fit in the range [0,"
             << Width - 1 << "] x [0," << Height - 1 << "]." << endl;
    }
    return &(Pixels[i][j]);
}

// int BMP::TellBitDepth( void ) const
int BMP::TellBitDepth(void)
{
    return BitDepth;
}

// int BMP::TellHeight( void ) const
int BMP::TellHeight(void)
{
    return Height;
}

// int BMP::TellWidth( void ) const
int BMP::TellWidth(void)
{
    return Width;
}

// int BMP::TellNumberOfColors( void ) const
int BMP::TellNumberOfColors(void)
{
    int output = IntPow(2, BitDepth);
    if (BitDepth == 32)
    {
        output = IntPow(2, 24);
    }
    return output;
}

bool BMP::SetBitDepth(int NewDepth)
{
    using namespace std;
    if (NewDepth != 1 && NewDepth != 4 && NewDepth != 8 && NewDepth != 16 &&
        NewDepth != 24 && NewDepth != 32)
    {
        if (EasyBMPwarnings)
        {
            cout << "EasyBMP Warning: User attempted to set unsupported bit "
                    "depth "
                 << NewDepth << "." << endl
                 << "                 Bit depth remains unchanged at "
                 << BitDepth << "." << endl;
        }
        return false;
    }

    BitDepth = NewDepth;
    if (Colors)
    {
        delete[] Colors;
    }
    int NumberOfColors = IntPow(2, BitDepth);
    if (BitDepth == 1 || BitDepth == 4 || BitDepth == 8)
    {
        Colors = new RGBApixel[NumberOfColors];
    }
    else
    {
        Colors = nullptr;
    }
    if (BitDepth == 1 || BitDepth == 4 || BitDepth == 8)
    {
        CreateStandardColorTable();
    }

    return true;
}

bool BMP::SetSize(int NewWidth, int NewHeight)
{
    using namespace std;
    if (NewWidth <= 0 || NewHeight <= 0)
    {
        if (EasyBMPwarnings)
        {
            cout << "EasyBMP Warning: User attempted to set a non-positive "
                    "width or height."
                 << endl
                 << "                 Size remains unchanged at " << Width
                 << " x " << Height << "." << endl;
        }
        return false;
    }

    int i, j;

    for (i = 0; i < Width; i++)
    {
        delete[] Pixels[i];
    }
    delete[] Pixels;

    Width = NewWidth;
    Height = NewHeight;
    Pixels = new RGBApixel*[Width];

    for (i = 0; i < Width; i++)
    {
        Pixels[i] = new RGBApixel[Height];
    }

    for (i = 0; i < Width; i++)
    {
        for (j = 0; j < Height; j++)
        {
            Pixels[i][j].Red = 255;
            Pixels[i][j].Green = 255;
            Pixels[i][j].Blue = 255;
            Pixels[i][j].Alpha = 0;
        }
    }

    return true;
}

bool BMP::WriteToFile(const char* FileName)
{
    using namespace std;
    if (!EasyBMPcheckDataSize())
    {
        if (EasyBMPwarnings)
        {
            cout << "EasyBMP Error: Data types are wrong size!" << endl
                 << "               You may need to mess with "
                    "EasyBMP_DataTypes.h"
                 << endl
                 << "               to fix these errors, and then recompile."
                 << endl
                 << "               All 32-bit and 64-bit machines should be"
                 << endl
                 << "               supported, however." << endl
                 << endl;
        }
        return false;
    }

    FILE* fp = fopen(FileName, "wb");
    if (fp == nullptr)
    {
        if (EasyBMPwarnings)
        {
            cout << "EasyBMP Error: Cannot open file " << FileName
                 << " for output." << endl;
        }
        return false;
    }

    // some preliminaries

    double dBytesPerPixel = ((double) BitDepth) / 8.0;
    double dBytesPerRow = dBytesPerPixel * (Width + 0.0);
    dBytesPerRow = ceil(dBytesPerRow);

    int BytePaddingPerRow = 4 - ((int) (dBytesPerRow)) % 4;
    if (BytePaddingPerRow == 4)
    {
        BytePaddingPerRow = 0;
    }

    double dActualBytesPerRow = dBytesPerRow + BytePaddingPerRow;

    double dTotalPixelBytes = Height * dActualBytesPerRow;

    double dPaletteSize = 0;
    if (BitDepth == 1 || BitDepth == 4 || BitDepth == 8)
    {
        dPaletteSize = IntPow(2, BitDepth) * 4.0;
    }

    // leave some room for 16-bit masks
    if (BitDepth == 16)
    {
        dPaletteSize = 3 * 4;
    }

    double dTotalFileSize = 14 + 40 + dPaletteSize + dTotalPixelBytes;

    // write the file header

    BMFH bmfh;
    bmfh.bfSize = (ebmpDWORD) dTotalFileSize;
    bmfh.bfReserved1 = 0;
    bmfh.bfReserved2 = 0;
    bmfh.bfOffBits = (ebmpDWORD)(14 + 40 + dPaletteSize);

    if (IsBigEndian())
    {
        bmfh.SwitchEndianess();
    }

    fwrite((char*) &(bmfh.bfType), sizeof(ebmpWORD), 1, fp);
    fwrite((char*) &(bmfh.bfSize), sizeof(ebmpDWORD), 1, fp);
    fwrite((char*) &(bmfh.bfReserved1), sizeof(ebmpWORD), 1, fp);
    fwrite((char*) &(bmfh.bfReserved2), sizeof(ebmpWORD), 1, fp);
    fwrite((char*) &(bmfh.bfOffBits), sizeof(ebmpDWORD), 1, fp);

    // write the info header

    BMIH bmih;
    bmih.biSize = 40;
    bmih.biWidth = Width;
    bmih.biHeight = Height;
    bmih.biPlanes = 1;
    bmih.biBitCount = BitDepth;
    bmih.biCompression = 0;
    bmih.biSizeImage = (ebmpDWORD) dTotalPixelBytes;
    if (XPelsPerMeter)
    {
        bmih.biXPelsPerMeter = XPelsPerMeter;
    }
    else
    {
        bmih.biXPelsPerMeter = DefaultXPelsPerMeter;
    }
    if (YPelsPerMeter)
    {
        bmih.biYPelsPerMeter = YPelsPerMeter;
    }
    else
    {
        bmih.biYPelsPerMeter = DefaultYPelsPerMeter;
    }

    bmih.biClrUsed = 0;
    bmih.biClrImportant = 0;

    // indicates that we'll be using bit fields for 16-bit files
    if (BitDepth == 16)
    {
        bmih.biCompression = 3;
    }

    if (IsBigEndian())
    {
        bmih.SwitchEndianess();
    }

    fwrite((char*) &(bmih.biSize), sizeof(ebmpDWORD), 1, fp);
    fwrite((char*) &(bmih.biWidth), sizeof(ebmpDWORD), 1, fp);
    fwrite((char*) &(bmih.biHeight), sizeof(ebmpDWORD), 1, fp);
    fwrite((char*) &(bmih.biPlanes), sizeof(ebmpWORD), 1, fp);
    fwrite((char*) &(bmih.biBitCount), sizeof(ebmpWORD), 1, fp);
    fwrite((char*) &(bmih.biCompression), sizeof(ebmpDWORD), 1, fp);
    fwrite((char*) &(bmih.biSizeImage), sizeof(ebmpDWORD), 1, fp);
    fwrite((char*) &(bmih.biXPelsPerMeter), sizeof(ebmpDWORD), 1, fp);
    fwrite((char*) &(bmih.biYPelsPerMeter), sizeof(ebmpDWORD), 1, fp);
    fwrite((char*) &(bmih.biClrUsed), sizeof(ebmpDWORD), 1, fp);
    fwrite((char*) &(bmih.biClrImportant), sizeof(ebmpDWORD), 1, fp);

    // write the palette
    if (BitDepth == 1 || BitDepth == 4 || BitDepth == 8)
    {
        int NumberOfColors = IntPow(2, BitDepth);

        // if there is no palette, create one
        if (!Colors)
        {
            if (!Colors)
            {
                Colors = new RGBApixel[NumberOfColors];
            }
            CreateStandardColorTable();
        }

        int n;
        for (n = 0; n < NumberOfColors; n++)
        {
            fwrite((char*) &(Colors[n]), 4, 1, fp);
        }
    }

    // write the pixels
    int i, j;
    if (BitDepth != 16)
    {
        ebmpBYTE* Buffer;
        int BufferSize = (int) ((Width * BitDepth) / 8.0);
        while (8 * BufferSize < Width * BitDepth)
        {
            BufferSize++;
        }
        while (BufferSize % 4)
        {
            BufferSize++;
        }

        Buffer = new ebmpBYTE[BufferSize];
        for (j = 0; j < BufferSize; j++)
        {
            Buffer[j] = 0;
        }

        j = Height - 1;

        while (j > -1)
        {
            bool Success = false;
            if (BitDepth == 32)
            {
                Success = Write32bitRow(Buffer, BufferSize, j);
            }
            if (BitDepth == 24)
            {
                Success = Write24bitRow(Buffer, BufferSize, j);
            }
            if (BitDepth == 8)
            {
                Success = Write8bitRow(Buffer, BufferSize, j);
            }
            if (BitDepth == 4)
            {
                Success = Write4bitRow(Buffer, BufferSize, j);
            }
            if (BitDepth == 1)
            {
                Success = Write1bitRow(Buffer, BufferSize, j);
            }
            if (Success)
            {
                int BytesWritten =
                    (int) fwrite((char*) Buffer, 1, BufferSize, fp);
                if (BytesWritten != BufferSize)
                {
                    Success = false;
                }
            }
            if (!Success)
            {
                if (EasyBMPwarnings)
                {
                    cout << "EasyBMP Error: Could not write proper amount of "
                            "data."
                         << endl;
                }
                j = -1;
            }
            j--;
        }

        delete[] Buffer;
    }

    if (BitDepth == 16)
    {
        // write the bit masks

        ebmpWORD BlueMask = 31;       // bits 12-16
        ebmpWORD GreenMask = 2016;    // bits 6-11
        ebmpWORD RedMask = 63488;     // bits 1-5
        ebmpWORD ZeroWORD = 0;

        if (IsBigEndian())
        {
            RedMask = FlipWORD(RedMask);
        }
        fwrite((char*) &RedMask, 2, 1, fp);
        fwrite((char*) &ZeroWORD, 2, 1, fp);

        if (IsBigEndian())
        {
            GreenMask = FlipWORD(GreenMask);
        }
        fwrite((char*) &GreenMask, 2, 1, fp);
        fwrite((char*) &ZeroWORD, 2, 1, fp);

        if (IsBigEndian())
        {
            BlueMask = FlipWORD(BlueMask);
        }
        fwrite((char*) &BlueMask, 2, 1, fp);
        fwrite((char*) &ZeroWORD, 2, 1, fp);

        int DataBytes = Width * 2;
        int PaddingBytes = (4 - DataBytes % 4) % 4;

        // write the actual pixels

        for (j = Height - 1; j >= 0; j--)
        {
            // write all row pixel data
            i = 0;
            int WriteNumber = 0;
            while (WriteNumber < DataBytes)
            {
                ebmpWORD TempWORD;

                ebmpWORD RedWORD = (ebmpWORD)((Pixels[i][j]).Red / 8);
                ebmpWORD GreenWORD = (ebmpWORD)((Pixels[i][j]).Green / 4);
                ebmpWORD BlueWORD = (ebmpWORD)((Pixels[i][j]).Blue / 8);

                TempWORD = (RedWORD << 11) + (GreenWORD << 5) + BlueWORD;
                if (IsBigEndian())
                {
                    TempWORD = FlipWORD(TempWORD);
                }

                fwrite((char*) &TempWORD, 2, 1, fp);
                WriteNumber += 2;
                i++;
            }
            // write any necessary row padding
            WriteNumber = 0;
            while (WriteNumber < PaddingBytes)
            {
                ebmpBYTE TempBYTE = 0;
                fwrite((char*) &TempBYTE, 1, 1, fp);
                WriteNumber++;
            }
        }
    }

    fclose(fp);
    return true;
}

bool BMP::ReadFromFile(const char* FileName)
{
    using namespace std;
    if (!EasyBMPcheckDataSize())
    {
        if (EasyBMPwarnings)
        {
            cout << "EasyBMP Error: Data types are wrong size!" << endl
                 << "               You may need to mess with "
                    "EasyBMP_DataTypes.h"
                 << endl
                 << "               to fix these errors, and then recompile."
                 << endl
                 << "               All 32-bit and 64-bit machines should be"
                 << endl
                 << "               supported, however." << endl
                 << endl;
        }
        return false;
    }

    FILE* fp = fopen(FileName, "rb");
    if (fp == nullptr)
    {
        if (EasyBMPwarnings)
        {
            cout << "EasyBMP Error: Cannot open file " << FileName
                 << " for input." << endl;
        }
        SetBitDepth(1);
        SetSize(1, 1);
        return false;
    }

    // read the file header

    BMFH bmfh;
    bool NotCorrupted = true;

    NotCorrupted &= SafeFread((char*) &(bmfh.bfType), sizeof(ebmpWORD), 1, fp);

    bool IsBitmap = false;

    if (IsBigEndian() && bmfh.bfType == 16973)
    {
        IsBitmap = true;
    }
    if (!IsBigEndian() && bmfh.bfType == 19778)
    {
        IsBitmap = true;
    }

    if (!IsBitmap)
    {
        if (EasyBMPwarnings)
        {
            cout << "EasyBMP Error: " << FileName
                 << " is not a Windows BMP file!" << endl;
        }
        fclose(fp);
        return false;
    }

    NotCorrupted &= SafeFread((char*) &(bmfh.bfSize), sizeof(ebmpDWORD), 1, fp);
    NotCorrupted &=
        SafeFread((char*) &(bmfh.bfReserved1), sizeof(ebmpWORD), 1, fp);
    NotCorrupted &=
        SafeFread((char*) &(bmfh.bfReserved2), sizeof(ebmpWORD), 1, fp);
    NotCorrupted &=
        SafeFread((char*) &(bmfh.bfOffBits), sizeof(ebmpDWORD), 1, fp);

    if (IsBigEndian())
    {
        bmfh.SwitchEndianess();
    }

    // read the info header

    BMIH bmih;

    NotCorrupted &= SafeFread((char*) &(bmih.biSize), sizeof(ebmpDWORD), 1, fp);
    NotCorrupted &=
        SafeFread((char*) &(bmih.biWidth), sizeof(ebmpDWORD), 1, fp);
    NotCorrupted &=
        SafeFread((char*) &(bmih.biHeight), sizeof(ebmpDWORD), 1, fp);
    NotCorrupted &=
        SafeFread((char*) &(bmih.biPlanes), sizeof(ebmpWORD), 1, fp);
    NotCorrupted &=
        SafeFread((char*) &(bmih.biBitCount), sizeof(ebmpWORD), 1, fp);

    NotCorrupted &=
        SafeFread((char*) &(bmih.biCompression), sizeof(ebmpDWORD), 1, fp);
    NotCorrupted &=
        SafeFread((char*) &(bmih.biSizeImage), sizeof(ebmpDWORD), 1, fp);
    NotCorrupted &=
        SafeFread((char*) &(bmih.biXPelsPerMeter), sizeof(ebmpDWORD), 1, fp);
    NotCorrupted &=
        SafeFread((char*) &(bmih.biYPelsPerMeter), sizeof(ebmpDWORD), 1, fp);
    NotCorrupted &=
        SafeFread((char*) &(bmih.biClrUsed), sizeof(ebmpDWORD), 1, fp);
    NotCorrupted &=
        SafeFread((char*) &(bmih.biClrImportant), sizeof(ebmpDWORD), 1, fp);

    if (IsBigEndian())
    {
        bmih.SwitchEndianess();
    }

    // a safety catch: if any of the header information didn't read properly, abort
    // future idea: check to see if at least most is self-consistent

    if (!NotCorrupted)
    {
        if (EasyBMPwarnings)
        {
            cout << "EasyBMP Error: " << FileName << " is obviously corrupted."
                 << endl;
        }
        SetSize(1, 1);
        SetBitDepth(1);
        fclose(fp);
        return false;
    }

    XPelsPerMeter = bmih.biXPelsPerMeter;
    YPelsPerMeter = bmih.biYPelsPerMeter;

    // if bmih.biCompression 1 or 2, then the file is RLE compressed

    if (bmih.biCompression == 1 || bmih.biCompression == 2)
    {
        if (EasyBMPwarnings)
        {
            cout << "EasyBMP Error: " << FileName << " is (RLE) compressed."
                 << endl
                 << "               EasyBMP does not support compression."
                 << endl;
        }
        SetSize(1, 1);
        SetBitDepth(1);
        fclose(fp);
        return false;
    }

    // if bmih.biCompression > 3, then something strange is going on
    // it's probably an OS2 bitmap file.

    if (bmih.biCompression > 3)
    {
        if (EasyBMPwarnings)
        {
            cout << "EasyBMP Error: " << FileName
                 << " is in an unsupported format." << endl
                 << "               (bmih.biCompression = "
                 << bmih.biCompression << ")" << endl
                 << "               The file is probably an old OS2 bitmap or "
                    "corrupted."
                 << endl;
        }
        SetSize(1, 1);
        SetBitDepth(1);
        fclose(fp);
        return false;
    }

    if (bmih.biCompression == 3 && bmih.biBitCount != 16)
    {
        if (EasyBMPwarnings)
        {
            cout << "EasyBMP Error: " << FileName
                 << " uses bit fields and is not a" << endl
                 << "               16-bit file. This is not supported."
                 << endl;
        }
        SetSize(1, 1);
        SetBitDepth(1);
        fclose(fp);
        return false;
    }

    // set the bit depth

    int TempBitDepth = (int) bmih.biBitCount;
    if (TempBitDepth != 1 && TempBitDepth != 4 && TempBitDepth != 8 &&
        TempBitDepth != 16 && TempBitDepth != 24 && TempBitDepth != 32)
    {
        if (EasyBMPwarnings)
        {
            cout << "EasyBMP Error: " << FileName
                 << " has unrecognized bit depth." << endl;
        }
        SetSize(1, 1);
        SetBitDepth(1);
        fclose(fp);
        return false;
    }
    SetBitDepth((int) bmih.biBitCount);

    // set the size

    if ((int) bmih.biWidth <= 0 || (int) bmih.biHeight <= 0)
    {
        if (EasyBMPwarnings)
        {
            cout << "EasyBMP Error: " << FileName
                 << " has a non-positive width or height." << endl;
        }
        SetSize(1, 1);
        SetBitDepth(1);
        fclose(fp);
        return false;
    }
    SetSize((int) bmih.biWidth, (int) bmih.biHeight);

    // if < 16 bits, read the palette

    if (BitDepth < 16)
    {
        // determine the number of colors specified in the
        // color table

        int NumberOfColorsToRead = ((int) bmfh.bfOffBits - 54) / 4;
        if (NumberOfColorsToRead > IntPow(2, BitDepth))
        {
            NumberOfColorsToRead = IntPow(2, BitDepth);
        }

        if (NumberOfColorsToRead < TellNumberOfColors())
        {
            if (EasyBMPwarnings)
            {
                cout << "EasyBMP Warning: file " << FileName
                     << " has an underspecified" << endl
                     << "                 color table. The table will be "
                        "padded with extra"
                     << endl
                     << "                 white (255,255,255,0) entries."
                     << endl;
            }
        }

        int n;
        for (n = 0; n < NumberOfColorsToRead; n++)
        {
            SafeFread((char*) &(Colors[n]), 4, 1, fp);
        }
        for (n = NumberOfColorsToRead; n < TellNumberOfColors(); n++)
        {
            RGBApixel WHITE;
            WHITE.Red = 255;
            WHITE.Green = 255;
            WHITE.Blue = 255;
            WHITE.Alpha = 0;
            SetColor(n, WHITE);
        }
    }

    // skip blank data if bfOffBits so indicates

    int BytesToSkip = static_cast<int>(bmfh.bfOffBits) - 54;
    ;
    if (BitDepth < 16)
    {
        BytesToSkip -= 4 * IntPow(2, BitDepth);
    }
    if (BitDepth == 16 && bmih.biCompression == 3)
    {
        BytesToSkip -= 3 * 4;
    }
    if (BytesToSkip < 0)
    {
        BytesToSkip = 0;
    }
    if (BytesToSkip > 0 && BitDepth != 16)
    {
        if (EasyBMPwarnings)
        {
            cout << "EasyBMP Warning: Extra meta data detected in file "
                 << FileName << endl
                 << "                 Data will be skipped." << endl;
        }
        ebmpBYTE* TempSkipBYTE;
        TempSkipBYTE = new ebmpBYTE[BytesToSkip];
        SafeFread((char*) TempSkipBYTE, BytesToSkip, 1, fp);
        delete[] TempSkipBYTE;
    }

    // This code reads 1, 4, 8, 24, and 32-bpp files
    // with a more-efficient buffered technique.

    int i, j;
    if (BitDepth != 16)
    {
        int BufferSize = (int) ((Width * BitDepth) / 8.0);
        while (8 * BufferSize < Width * BitDepth)
        {
            BufferSize++;
        }
        while (BufferSize % 4)
        {
            BufferSize++;
        }
        ebmpBYTE* Buffer;
        Buffer = new ebmpBYTE[BufferSize];
        j = Height - 1;
        while (j > -1)
        {
            int BytesRead = (int) fread((char*) Buffer, 1, BufferSize, fp);
            if (BytesRead < BufferSize)
            {
                j = -1;
                if (EasyBMPwarnings)
                {
                    cout << "EasyBMP Error: Could not read proper amount of "
                            "data."
                         << endl;
                }
            }
            else
            {
                bool Success = false;
                if (BitDepth == 1)
                {
                    Success = Read1bitRow(Buffer, BufferSize, j);
                }
                if (BitDepth == 4)
                {
                    Success = Read4bitRow(Buffer, BufferSize, j);
                }
                if (BitDepth == 8)
                {
                    Success = Read8bitRow(Buffer, BufferSize, j);
                }
                if (BitDepth == 24)
                {
                    Success = Read24bitRow(Buffer, BufferSize, j);
                }
                if (BitDepth == 32)
                {
                    Success = Read32bitRow(Buffer, BufferSize, j);
                }
                if (!Success)
                {
                    if (EasyBMPwarnings)
                    {
                        cout << "EasyBMP Error: Could not read enough pixel "
                                "data!"
                             << endl;
                    }
                    j = -1;
                }
            }
            j--;
        }
        delete[] Buffer;
    }

    if (BitDepth == 16)
    {
        int DataBytes = Width * 2;
        int PaddingBytes = (4 - DataBytes % 4) % 4;

        // set the default mask

        ebmpWORD BlueMask = 31;      // bits 12-16
        ebmpWORD GreenMask = 992;    // bits 7-11
        ebmpWORD RedMask = 31744;    // bits 2-6

        // read the bit fields, if necessary, to
        // override the default 5-5-5 mask

        if (bmih.biCompression != 0)
        {
            // read the three bit masks

            ebmpWORD TempMaskWORD;

            SafeFread((char*) &RedMask, 2, 1, fp);
            if (IsBigEndian())
            {
                RedMask = FlipWORD(RedMask);
            }
            SafeFread((char*) &TempMaskWORD, 2, 1, fp);

            SafeFread((char*) &GreenMask, 2, 1, fp);
            if (IsBigEndian())
            {
                GreenMask = FlipWORD(GreenMask);
            }
            SafeFread((char*) &TempMaskWORD, 2, 1, fp);

            SafeFread((char*) &BlueMask, 2, 1, fp);
            if (IsBigEndian())
            {
                BlueMask = FlipWORD(BlueMask);
            }
            SafeFread((char*) &TempMaskWORD, 2, 1, fp);
        }

        // read and skip any meta data

        if (BytesToSkip > 0)
        {
            if (EasyBMPwarnings)
            {
                cout << "EasyBMP Warning: Extra meta data detected in file "
                     << FileName << endl
                     << "                 Data will be skipped." << endl;
            }
            ebmpBYTE* TempSkipBYTE;
            TempSkipBYTE = new ebmpBYTE[BytesToSkip];
            SafeFread((char*) TempSkipBYTE, BytesToSkip, 1, fp);
            delete[] TempSkipBYTE;
        }

        // determine the red, green and blue shifts

        int GreenShift = 0;
        ebmpWORD TempShiftWORD = GreenMask;
        while (TempShiftWORD > 31)
        {
            TempShiftWORD = TempShiftWORD >> 1;
            GreenShift++;
        }
        int BlueShift = 0;
        TempShiftWORD = BlueMask;
        while (TempShiftWORD > 31)
        {
            TempShiftWORD = TempShiftWORD >> 1;
            BlueShift++;
        }
        int RedShift = 0;
        TempShiftWORD = RedMask;
        while (TempShiftWORD > 31)
        {
            TempShiftWORD = TempShiftWORD >> 1;
            RedShift++;
        }

        // read the actual pixels

        for (j = Height - 1; j >= 0; j--)
        {
            i = 0;
            int ReadNumber = 0;
            while (ReadNumber < DataBytes)
            {
                ebmpWORD TempWORD;
                SafeFread((char*) &TempWORD, 2, 1, fp);
                if (IsBigEndian())
                {
                    TempWORD = FlipWORD(TempWORD);
                }
                ReadNumber += 2;

                ebmpWORD Red = RedMask & TempWORD;
                ebmpWORD Green = GreenMask & TempWORD;
                ebmpWORD Blue = BlueMask & TempWORD;

                ebmpBYTE BlueBYTE = (ebmpBYTE) 8 * (Blue >> BlueShift);
                ebmpBYTE GreenBYTE = (ebmpBYTE) 8 * (Green >> GreenShift);
                ebmpBYTE RedBYTE = (ebmpBYTE) 8 * (Red >> RedShift);

                (Pixels[i][j]).Red = RedBYTE;
                (Pixels[i][j]).Green = GreenBYTE;
                (Pixels[i][j]).Blue = BlueBYTE;

                i++;
            }
            ReadNumber = 0;
            while (ReadNumber < PaddingBytes)
            {
                ebmpBYTE TempBYTE;
                SafeFread((char*) &TempBYTE, 1, 1, fp);
                ReadNumber++;
            }
        }
    }

    fclose(fp);
    return true;
}

bool BMP::CreateStandardColorTable(void)
{
    using namespace std;
    if (BitDepth != 1 && BitDepth != 4 && BitDepth != 8)
    {
        if (EasyBMPwarnings)
        {
            cout
                << "EasyBMP Warning: Attempted to create color table at a bit"
                << endl
                << "                 depth that does not require a color table."
                << endl
                << "                 Ignoring request." << endl;
        }
        return false;
    }

    if (BitDepth == 1)
    {
        int i;
        for (i = 0; i < 2; i++)
        {
            Colors[i].Red = i * 255;
            Colors[i].Green = i * 255;
            Colors[i].Blue = i * 255;
            Colors[i].Alpha = 0;
        }
        return true;
    }

    if (BitDepth == 4)
    {
        int i = 0;
        int j, k, ell;

        // simplify the code for the first 8 colors
        for (ell = 0; ell < 2; ell++)
        {
            for (k = 0; k < 2; k++)
            {
                for (j = 0; j < 2; j++)
                {
                    Colors[i].Red = j * 128;
                    Colors[i].Green = k * 128;
                    Colors[i].Blue = ell * 128;
                    i++;
                }
            }
        }

        // simplify the code for the last 8 colors
        for (ell = 0; ell < 2; ell++)
        {
            for (k = 0; k < 2; k++)
            {
                for (j = 0; j < 2; j++)
                {
                    Colors[i].Red = j * 255;
                    Colors[i].Green = k * 255;
                    Colors[i].Blue = ell * 255;
                    i++;
                }
            }
        }

        // overwrite the duplicate color
        i = 8;
        Colors[i].Red = 192;
        Colors[i].Green = 192;
        Colors[i].Blue = 192;

        for (i = 0; i < 16; i++)
        {
            Colors[i].Alpha = 0;
        }
        return true;
    }

    if (BitDepth == 8)
    {
        int i = 0;
        int j, k, ell;

        // do an easy loop, which works for all but colors
        // 0 to 9 and 246 to 255
        for (ell = 0; ell < 4; ell++)
        {
            for (k = 0; k < 8; k++)
            {
                for (j = 0; j < 8; j++)
                {
                    Colors[i].Red = j * 32;
                    Colors[i].Green = k * 32;
                    Colors[i].Blue = ell * 64;
                    Colors[i].Alpha = 0;
                    i++;
                }
            }
        }

        // now redo the first 8 colors
        i = 0;
        for (ell = 0; ell < 2; ell++)
        {
            for (k = 0; k < 2; k++)
            {
                for (j = 0; j < 2; j++)
                {
                    Colors[i].Red = j * 128;
                    Colors[i].Green = k * 128;
                    Colors[i].Blue = ell * 128;
                    i++;
                }
            }
        }

        // overwrite colors 7, 8, 9
        i = 7;
        Colors[i].Red = 192;
        Colors[i].Green = 192;
        Colors[i].Blue = 192;
        i++;    // 8
        Colors[i].Red = 192;
        Colors[i].Green = 220;
        Colors[i].Blue = 192;
        i++;    // 9
        Colors[i].Red = 166;
        Colors[i].Green = 202;
        Colors[i].Blue = 240;

        // overwrite colors 246 to 255
        i = 246;
        Colors[i].Red = 255;
        Colors[i].Green = 251;
        Colors[i].Blue = 240;
        i++;    // 247
        Colors[i].Red = 160;
        Colors[i].Green = 160;
        Colors[i].Blue = 164;
        i++;    // 248
        Colors[i].Red = 128;
        Colors[i].Green = 128;
        Colors[i].Blue = 128;
        i++;    // 249
        Colors[i].Red = 255;
        Colors[i].Green = 0;
        Colors[i].Blue = 0;
        i++;    // 250
        Colors[i].Red = 0;
        Colors[i].Green = 255;
        Colors[i].Blue = 0;
        i++;    // 251
        Colors[i].Red = 255;
        Colors[i].Green = 255;
        Colors[i].Blue = 0;
        i++;    // 252
        Colors[i].Red = 0;
        Colors[i].Green = 0;
        Colors[i].Blue = 255;
        i++;    // 253
        Colors[i].Red = 255;
        Colors[i].Green = 0;
        Colors[i].Blue = 255;
        i++;    // 254
        Colors[i].Red = 0;
        Colors[i].Green = 255;
        Colors[i].Blue = 255;
        i++;    // 255
        Colors[i].Red = 255;
        Colors[i].Green = 255;
        Colors[i].Blue = 255;

        return true;
    }
    return true;
}

bool SafeFread(char* buffer, int size, int number, FILE* fp)
{
    using namespace std;
    int ItemsRead;
    if (feof(fp))
    {
        return false;
    }
    ItemsRead = (int) fread(buffer, size, number, fp);
    if (ItemsRead < number)
    {
        return false;
    }
    return true;
}

void BMP::SetDPI(int HorizontalDPI, int VerticalDPI)
{
    XPelsPerMeter = (int) (HorizontalDPI * 39.37007874015748);
    YPelsPerMeter = (int) (VerticalDPI * 39.37007874015748);
}

// int BMP::TellVerticalDPI( void ) const
int BMP::TellVerticalDPI(void)
{
    if (!YPelsPerMeter)
    {
        YPelsPerMeter = DefaultYPelsPerMeter;
    }
    return (int) (YPelsPerMeter / (double) 39.37007874015748);
}

// int BMP::TellHorizontalDPI( void ) const
int BMP::TellHorizontalDPI(void)
{
    if (!XPelsPerMeter)
    {
        XPelsPerMeter = DefaultXPelsPerMeter;
    }
    return (int) (XPelsPerMeter / (double) 39.37007874015748);
}

/* These functions are defined in EasyBMP_VariousBMPutilities.h */

BMFH GetBMFH(const char* szFileNameIn)
{
    using namespace std;
    BMFH bmfh;

    FILE* fp;
    fp = fopen(szFileNameIn, "rb");

    if (!fp)
    {
        if (EasyBMPwarnings)
        {
            cout << "EasyBMP Error: Cannot initialize from file "
                 << szFileNameIn << "." << endl
                 << "               File cannot be opened or does not exist."
                 << endl;
        }
        bmfh.bfType = 0;
        return bmfh;
    }

    SafeFread((char*) &(bmfh.bfType), sizeof(ebmpWORD), 1, fp);
    SafeFread((char*) &(bmfh.bfSize), sizeof(ebmpDWORD), 1, fp);
    SafeFread((char*) &(bmfh.bfReserved1), sizeof(ebmpWORD), 1, fp);
    SafeFread((char*) &(bmfh.bfReserved2), sizeof(ebmpWORD), 1, fp);
    SafeFread((char*) &(bmfh.bfOffBits), sizeof(ebmpDWORD), 1, fp);

    fclose(fp);

    if (IsBigEndian())
    {
        bmfh.SwitchEndianess();
    }

    return bmfh;
}

BMIH GetBMIH(const char* szFileNameIn)
{
    using namespace std;
    BMFH bmfh;
    BMIH bmih;

    FILE* fp;
    fp = fopen(szFileNameIn, "rb");

    if (!fp)
    {
        if (EasyBMPwarnings)
        {
            cout << "EasyBMP Error: Cannot initialize from file "
                 << szFileNameIn << "." << endl
                 << "               File cannot be opened or does not exist."
                 << endl;
        }
        return bmih;
    }

    // read the bmfh, i.e., first 14 bytes (just to get it out of the way);

    ebmpBYTE TempBYTE;
    int i;
    for (i = 14; i > 0; i--)
    {
        SafeFread((char*) &TempBYTE, sizeof(ebmpBYTE), 1, fp);
    }

    // read the bmih

    SafeFread((char*) &(bmih.biSize), sizeof(ebmpDWORD), 1, fp);
    SafeFread((char*) &(bmih.biWidth), sizeof(ebmpDWORD), 1, fp);
    SafeFread((char*) &(bmih.biHeight), sizeof(ebmpDWORD), 1, fp);
    SafeFread((char*) &(bmih.biPlanes), sizeof(ebmpWORD), 1, fp);

    SafeFread((char*) &(bmih.biBitCount), sizeof(ebmpWORD), 1, fp);
    SafeFread((char*) &(bmih.biCompression), sizeof(ebmpDWORD), 1, fp);
    SafeFread((char*) &(bmih.biSizeImage), sizeof(ebmpDWORD), 1, fp);
    SafeFread((char*) &(bmih.biXPelsPerMeter), sizeof(ebmpDWORD), 1, fp);

    SafeFread((char*) &(bmih.biYPelsPerMeter), sizeof(ebmpDWORD), 1, fp);
    SafeFread((char*) &(bmih.biClrUsed), sizeof(ebmpDWORD), 1, fp);
    SafeFread((char*) &(bmih.biClrImportant), sizeof(ebmpDWORD), 1, fp);

    fclose(fp);

    if (IsBigEndian())
    {
        bmih.SwitchEndianess();
    }

    return bmih;
}

void DisplayBitmapInfo(const char* szFileNameIn)
{
    using namespace std;
    FILE* fp;
    fp = fopen(szFileNameIn, "rb");

    if (!fp)
    {
        if (EasyBMPwarnings)
        {
            cout << "EasyBMP Error: Cannot initialize from file "
                 << szFileNameIn << "." << endl
                 << "               File cannot be opened or does not exist."
                 << endl;
        }
        return;
    }
    fclose(fp);

    // don't duplicate work! Just use the functions from above!

    BMFH bmfh = GetBMFH(szFileNameIn);
    BMIH bmih = GetBMIH(szFileNameIn);

    cout << "File information for file " << szFileNameIn << ":" << endl << endl;

    cout << "BITMAPFILEHEADER:" << endl
         << "bfType: " << bmfh.bfType << endl
         << "bfSize: " << bmfh.bfSize << endl
         << "bfReserved1: " << bmfh.bfReserved1 << endl
         << "bfReserved2: " << bmfh.bfReserved2 << endl
         << "bfOffBits: " << bmfh.bfOffBits << endl
         << endl;

    cout << "BITMAPINFOHEADER:" << endl
         << "biSize: " << bmih.biSize << endl
         << "biWidth: " << bmih.biWidth << endl
         << "biHeight: " << bmih.biHeight << endl
         << "biPlanes: " << bmih.biPlanes << endl
         << "biBitCount: " << bmih.biBitCount << endl
         << "biCompression: " << bmih.biCompression << endl
         << "biSizeImage: " << bmih.biSizeImage << endl
         << "biXPelsPerMeter: " << bmih.biXPelsPerMeter << endl
         << "biYPelsPerMeter: " << bmih.biYPelsPerMeter << endl
         << "biClrUsed: " << bmih.biClrUsed << endl
         << "biClrImportant: " << bmih.biClrImportant << endl
         << endl;
    return;
}

int GetBitmapColorDepth(const char* szFileNameIn)
{
    BMIH bmih = GetBMIH(szFileNameIn);
    return (int) bmih.biBitCount;
}

void PixelToPixelCopy(
    BMP& From, int FromX, int FromY, BMP& To, int ToX, int ToY)
{
    *To(ToX, ToY) = *From(FromX, FromY);
    return;
}

void PixelToPixelCopyTransparent(BMP& From, int FromX, int FromY, BMP& To,
    int ToX, int ToY, RGBApixel& Transparent)
{
    if (From(FromX, FromY)->Red != Transparent.Red ||
        From(FromX, FromY)->Green != Transparent.Green ||
        From(FromX, FromY)->Blue != Transparent.Blue)
    {
        *To(ToX, ToY) = *From(FromX, FromY);
    }
    return;
}

void RangedPixelToPixelCopy(BMP& From, int FromL, int FromR, int FromB,
    int FromT, BMP& To, int ToX, int ToY)
{
    // make sure the conventions are followed
    if (FromB < FromT)
    {
        int Temp = FromT;
        FromT = FromB;
        FromB = Temp;
    }

    // make sure that the copied regions exist in both bitmaps
    if (FromR >= From.TellWidth())
    {
        FromR = From.TellWidth() - 1;
    }
    if (FromL < 0)
    {
        FromL = 0;
    }

    if (FromB >= From.TellHeight())
    {
        FromB = From.TellHeight() - 1;
    }
    if (FromT < 0)
    {
        FromT = 0;
    }

    if (ToX + (FromR - FromL) >= To.TellWidth())
    {
        FromR = To.TellWidth() - 1 + FromL - ToX;
    }
    if (ToY + (FromB - FromT) >= To.TellHeight())
    {
        FromB = To.TellHeight() - 1 + FromT - ToY;
    }

    int i, j;
    for (j = FromT; j <= FromB; j++)
    {
        for (i = FromL; i <= FromR; i++)
        {
            PixelToPixelCopy(
                From, i, j, To, ToX + (i - FromL), ToY + (j - FromT));
        }
    }

    return;
}

void RangedPixelToPixelCopyTransparent(BMP& From, int FromL, int FromR,
    int FromB, int FromT, BMP& To, int ToX, int ToY, RGBApixel& Transparent)
{
    // make sure the conventions are followed
    if (FromB < FromT)
    {
        int Temp = FromT;
        FromT = FromB;
        FromB = Temp;
    }

    // make sure that the copied regions exist in both bitmaps
    if (FromR >= From.TellWidth())
    {
        FromR = From.TellWidth() - 1;
    }
    if (FromL < 0)
    {
        FromL = 0;
    }

    if (FromB >= From.TellHeight())
    {
        FromB = From.TellHeight() - 1;
    }
    if (FromT < 0)
    {
        FromT = 0;
    }

    if (ToX + (FromR - FromL) >= To.TellWidth())
    {
        FromR = To.TellWidth() - 1 + FromL - ToX;
    }
    if (ToY + (FromB - FromT) >= To.TellHeight())
    {
        FromB = To.TellHeight() - 1 + FromT - ToY;
    }

    int i, j;
    for (j = FromT; j <= FromB; j++)
    {
        for (i = FromL; i <= FromR; i++)
        {
            PixelToPixelCopyTransparent(From, i, j, To, ToX + (i - FromL),
                ToY + (j - FromT), Transparent);
        }
    }

    return;
}

bool CreateGrayscaleColorTable(BMP& InputImage)
{
    using namespace std;
    int BitDepth = InputImage.TellBitDepth();
    if (BitDepth != 1 && BitDepth != 4 && BitDepth != 8)
    {
        if (EasyBMPwarnings)
        {
            cout
                << "EasyBMP Warning: Attempted to create color table at a bit"
                << endl
                << "                 depth that does not require a color table."
                << endl
                << "                 Ignoring request." << endl;
        }
        return false;
    }
    int i;
    int NumberOfColors = InputImage.TellNumberOfColors();

    ebmpBYTE StepSize;
    if (BitDepth != 1)
    {
        StepSize = 255 / (NumberOfColors - 1);
    }
    else
    {
        StepSize = 255;
    }

    for (i = 0; i < NumberOfColors; i++)
    {
        ebmpBYTE TempBYTE = i * StepSize;
        RGBApixel TempColor;
        TempColor.Red = TempBYTE;
        TempColor.Green = TempBYTE;
        TempColor.Blue = TempBYTE;
        TempColor.Alpha = 0;
        InputImage.SetColor(i, TempColor);
    }
    return true;
}

bool BMP::Read32bitRow(ebmpBYTE* Buffer, int BufferSize, int Row)
{
    int i;
    if (Width * 4 > BufferSize)
    {
        return false;
    }
    for (i = 0; i < Width; i++)
    {
        memcpy((char*) &(Pixels[i][Row]), (char*) Buffer + 4 * i, 4);
    }
    return true;
}

bool BMP::Read24bitRow(ebmpBYTE* Buffer, int BufferSize, int Row)
{
    int i;
    if (Width * 3 > BufferSize)
    {
        return false;
    }
    for (i = 0; i < Width; i++)
    {
        memcpy((char*) &(Pixels[i][Row]), Buffer + 3 * i, 3);
    }
    return true;
}

bool BMP::Read8bitRow(ebmpBYTE* Buffer, int BufferSize, int Row)
{
    int i;
    if (Width > BufferSize)
    {
        return false;
    }
    for (i = 0; i < Width; i++)
    {
        int Index = Buffer[i];
        *(this->operator()(i, Row)) = GetColor(Index);
    }
    return true;
}

bool BMP::Read4bitRow(ebmpBYTE* Buffer, int BufferSize, int Row)
{
    int Shifts[2] = {4, 0};
    int Masks[2] = {240, 15};

    int i = 0;
    int j;
    int k = 0;
    if (Width > 2 * BufferSize)
    {
        return false;
    }
    while (i < Width)
    {
        j = 0;
        while (j < 2 && i < Width)
        {
            int Index = (int) ((Buffer[k] & Masks[j]) >> Shifts[j]);
            *(this->operator()(i, Row)) = GetColor(Index);
            i++;
            j++;
        }
        k++;
    }
    return true;
}
bool BMP::Read1bitRow(ebmpBYTE* Buffer, int BufferSize, int Row)
{
    int Shifts[8] = {7, 6, 5, 4, 3, 2, 1, 0};
    int Masks[8] = {128, 64, 32, 16, 8, 4, 2, 1};

    int i = 0;
    int j;
    int k = 0;

    if (Width > 8 * BufferSize)
    {
        return false;
    }
    while (i < Width)
    {
        j = 0;
        while (j < 8 && i < Width)
        {
            int Index = (int) ((Buffer[k] & Masks[j]) >> Shifts[j]);
            *(this->operator()(i, Row)) = GetColor(Index);
            i++;
            j++;
        }
        k++;
    }
    return true;
}

bool BMP::Write32bitRow(ebmpBYTE* Buffer, int BufferSize, int Row)
{
    int i;
    if (Width * 4 > BufferSize)
    {
        return false;
    }
    for (i = 0; i < Width; i++)
    {
        memcpy((char*) Buffer + 4 * i, (char*) &(Pixels[i][Row]), 4);
    }
    return true;
}

bool BMP::Write24bitRow(ebmpBYTE* Buffer, int BufferSize, int Row)
{
    int i;
    if (Width * 3 > BufferSize)
    {
        return false;
    }
    for (i = 0; i < Width; i++)
    {
        memcpy((char*) Buffer + 3 * i, (char*) &(Pixels[i][Row]), 3);
    }
    return true;
}

bool BMP::Write8bitRow(ebmpBYTE* Buffer, int BufferSize, int Row)
{
    int i;
    if (Width > BufferSize)
    {
        return false;
    }
    for (i = 0; i < Width; i++)
    {
        Buffer[i] = FindClosestColor(Pixels[i][Row]);
    }
    return true;
}

bool BMP::Write4bitRow(ebmpBYTE* Buffer, int BufferSize, int Row)
{
    int PositionWeights[2] = {16, 1};

    int i = 0;
    int j;
    int k = 0;
    if (Width > 2 * BufferSize)
    {
        return false;
    }
    while (i < Width)
    {
        j = 0;
        int Index = 0;
        while (j < 2 && i < Width)
        {
            Index +=
                (PositionWeights[j] * (int) FindClosestColor(Pixels[i][Row]));
            i++;
            j++;
        }
        Buffer[k] = (ebmpBYTE) Index;
        k++;
    }
    return true;
}

bool BMP::Write1bitRow(ebmpBYTE* Buffer, int BufferSize, int Row)
{
    int PositionWeights[8] = {128, 64, 32, 16, 8, 4, 2, 1};

    int i = 0;
    int j;
    int k = 0;
    if (Width > 8 * BufferSize)
    {
        return false;
    }
    while (i < Width)
    {
        j = 0;
        int Index = 0;
        while (j < 8 && i < Width)
        {
            Index +=
                (PositionWeights[j] * (int) FindClosestColor(Pixels[i][Row]));
            i++;
            j++;
        }
        Buffer[k] = (ebmpBYTE) Index;
        k++;
    }
    return true;
}

ebmpBYTE BMP::FindClosestColor(RGBApixel& input)
{
    using namespace std;

    int i = 0;
    int NumberOfColors = TellNumberOfColors();
    ebmpBYTE BestI = 0;
    int BestMatch = 999999;

    while (i < NumberOfColors)
    {
        RGBApixel Attempt = GetColor(i);
        int TempMatch = IntSquare((int) Attempt.Red - (int) input.Red) +
            IntSquare((int) Attempt.Green - (int) input.Green) +
            IntSquare((int) Attempt.Blue - (int) input.Blue);
        if (TempMatch < BestMatch)
        {
            BestI = (ebmpBYTE) i;
            BestMatch = TempMatch;
        }
        if (BestMatch < 1)
        {
            break;
        }
        i++;
    }
    return BestI;
}

bool EasyBMPcheckDataSize(void)
{
    using namespace std;
    bool ReturnValue = true;
    if (sizeof(ebmpBYTE) != 1)
    {
        if (EasyBMPwarnings)
        {
            cout << "EasyBMP Error: ebmpBYTE has the wrong size ("
                 << sizeof(ebmpBYTE) << " bytes)," << endl
                 << "               Compared to the expected 1 byte value"
                 << endl;
        }
        ReturnValue = false;
    }
    if (sizeof(ebmpWORD) != 2)
    {
        if (EasyBMPwarnings)
        {
            cout << "EasyBMP Error: ebmpWORD has the wrong size ("
                 << sizeof(ebmpWORD) << " bytes)," << endl
                 << "               Compared to the expected 2 byte value"
                 << endl;
        }
        ReturnValue = false;
    }
    if (sizeof(ebmpDWORD) != 4)
    {
        if (EasyBMPwarnings)
        {
            cout << "EasyBMP Error: ebmpDWORD has the wrong size ("
                 << sizeof(ebmpDWORD) << " bytes)," << endl
                 << "               Compared to the expected 4 byte value"
                 << endl;
        }
        ReturnValue = false;
    }
    return ReturnValue;
}

bool Rescale(BMP& InputImage, char mode, int NewDimension)
{
    using namespace std;
    int CapMode = toupper(mode);

    BMP OldImage(InputImage);

    if (CapMode != 'P' && CapMode != 'W' && CapMode != 'H' && CapMode != 'F')
    {
        if (EasyBMPwarnings)
        {
            char ErrorMessage[1024];
            sprintf(ErrorMessage,
                "EasyBMP Error: Unknown rescale mode %c requested\n", mode);
            cout << ErrorMessage;
        }
        return false;
    }

    int NewWidth = 0;
    int NewHeight = 0;

    int OldWidth = OldImage.TellWidth();
    int OldHeight = OldImage.TellHeight();

    if (CapMode == 'P')
    {
        NewWidth = (int) floor(OldWidth * NewDimension / 100.0);
        NewHeight = (int) floor(OldHeight * NewDimension / 100.0);
    }
    if (CapMode == 'F')
    {
        if (OldWidth > OldHeight)
        {
            CapMode = 'W';
        }
        else
        {
            CapMode = 'H';
        }
    }

    if (CapMode == 'W')
    {
        double percent = (double) NewDimension / (double) OldWidth;
        NewWidth = NewDimension;
        NewHeight = (int) floor(OldHeight * percent);
    }
    if (CapMode == 'H')
    {
        double percent = (double) NewDimension / (double) OldHeight;
        NewHeight = NewDimension;
        NewWidth = (int) floor(OldWidth * percent);
    }

    if (NewWidth < 1)
    {
        NewWidth = 1;
    }
    if (NewHeight < 1)
    {
        NewHeight = 1;
    }

    InputImage.SetSize(NewWidth, NewHeight);
    InputImage.SetBitDepth(24);

    int I, J;
    double ThetaI, ThetaJ;

    for (int j = 0; j < NewHeight - 1; j++)
    {
        ThetaJ = (double) (j * (OldHeight - 1.0)) / (double) (NewHeight - 1.0);
        J = (int) floor(ThetaJ);
        ThetaJ -= J;

        for (int i = 0; i < NewWidth - 1; i++)
        {
            ThetaI =
                (double) (i * (OldWidth - 1.0)) / (double) (NewWidth - 1.0);
            I = (int) floor(ThetaI);
            ThetaI -= I;

            InputImage(i, j)->Red =
                (ebmpBYTE)((1.0 - ThetaI - ThetaJ + ThetaI * ThetaJ) *
                        (OldImage(I, J)->Red) +
                    (ThetaI - ThetaI * ThetaJ) * (OldImage(I + 1, J)->Red) +
                    (ThetaJ - ThetaI * ThetaJ) * (OldImage(I, J + 1)->Red) +
                    (ThetaI * ThetaJ) * (OldImage(I + 1, J + 1)->Red));
            InputImage(i, j)->Green =
                (ebmpBYTE)((1.0 - ThetaI - ThetaJ + ThetaI * ThetaJ) *
                        OldImage(I, J)->Green +
                    (ThetaI - ThetaI * ThetaJ) * OldImage(I + 1, J)->Green +
                    (ThetaJ - ThetaI * ThetaJ) * OldImage(I, J + 1)->Green +
                    (ThetaI * ThetaJ) * OldImage(I + 1, J + 1)->Green);
            InputImage(i, j)->Blue =
                (ebmpBYTE)((1.0 - ThetaI - ThetaJ + ThetaI * ThetaJ) *
                        OldImage(I, J)->Blue +
                    (ThetaI - ThetaI * ThetaJ) * OldImage(I + 1, J)->Blue +
                    (ThetaJ - ThetaI * ThetaJ) * OldImage(I, J + 1)->Blue +
                    (ThetaI * ThetaJ) * OldImage(I + 1, J + 1)->Blue);
        }
        InputImage(NewWidth - 1, j)->Red =
            (ebmpBYTE)((1.0 - ThetaJ) * (OldImage(OldWidth - 1, J)->Red) +
                ThetaJ * (OldImage(OldWidth - 1, J + 1)->Red));
        InputImage(NewWidth - 1, j)->Green =
            (ebmpBYTE)((1.0 - ThetaJ) * (OldImage(OldWidth - 1, J)->Green) +
                ThetaJ * (OldImage(OldWidth - 1, J + 1)->Green));
        InputImage(NewWidth - 1, j)->Blue =
            (ebmpBYTE)((1.0 - ThetaJ) * (OldImage(OldWidth - 1, J)->Blue) +
                ThetaJ * (OldImage(OldWidth - 1, J + 1)->Blue));
    }

    for (int i = 0; i < NewWidth - 1; i++)
    {
        ThetaI = (double) (i * (OldWidth - 1.0)) / (double) (NewWidth - 1.0);
        I = (int) floor(ThetaI);
        ThetaI -= I;
        InputImage(i, NewHeight - 1)->Red =
            (ebmpBYTE)((1.0 - ThetaI) * (OldImage(I, OldHeight - 1)->Red) +
                ThetaI * (OldImage(I, OldHeight - 1)->Red));
        InputImage(i, NewHeight - 1)->Green =
            (ebmpBYTE)((1.0 - ThetaI) * (OldImage(I, OldHeight - 1)->Green) +
                ThetaI * (OldImage(I, OldHeight - 1)->Green));
        InputImage(i, NewHeight - 1)->Blue =
            (ebmpBYTE)((1.0 - ThetaI) * (OldImage(I, OldHeight - 1)->Blue) +
                ThetaI * (OldImage(I, OldHeight - 1)->Blue));
    }

    *InputImage(NewWidth - 1, NewHeight - 1) =
        *OldImage(OldWidth - 1, OldHeight - 1);
    return true;
}
